
// === uuMix.string ===
// depend: uuMeta
/*
String.prototype.fmt(var_args, ...) - return "sprintf formatted string"
 */
(function uuMixStringScope() {
var _NUMBER  = 0x0001,
    _FLOAT   = 0x0002,
    _LITERAL = 0x0004,
    _SIGNED  = 0x0010,
    _UNSIGNED= 0x0020,
    _PREFIX  = 0x0040,
    _PRECIS  = 0x0080, // precision
    _OCTET   = 0x0100,
    _HEX     = 0x0200,
    _NOINDEX = 0x0400,
    _NOWIDTH = 0x0800,
    _TOUPPER = 0x1000,
    _TONUM   = 0x2000, // to number
    _TOASCII = 0x4000, // to printable
    _IGZERO  = 0x8000, // ignore zero flag (eg: "%05d" -> "%5d")
    _DECODE  = {
      i: _NUMBER | _SIGNED | _IGZERO,
      d: _NUMBER | _SIGNED | _IGZERO,
      u: _NUMBER | _UNSIGNED | _IGZERO,
      o: _NUMBER | _UNSIGNED | _IGZERO | _PREFIX | _OCTET,
      x: _NUMBER | _UNSIGNED | _IGZERO | _PREFIX | _HEX,
      X: _NUMBER | _UNSIGNED | _IGZERO | _PREFIX | _HEX | _TOUPPER,
      f: _FLOAT | _SIGNED | _PRECIS,
      c: _TONUM | _NOWIDTH,
      A: _TOASCII | _NOWIDTH,
      s: _LITERAL | _PRECIS,
      "%": _LITERAL | _NOINDEX | _NOWIDTH
    },
    _PARSE_FORMAT = /%(?:(\d+)\$)?(#|0)?(\d+)?(?:\.(\d+))?(l)?([%iduoxXfcAs])/g,
    _UNSIGNED_NUM = 0x100000000;

// String.prototype.fmt - sprintf (PHP::sprintf like function)
function StringFormat(var_args) { // @param Mix: sprintf args
                               // @return String: formated string
  var next = 1, idx = 0, format = this, av = arguments,
      // alias
      charCode = String.fromCharCode;

  return format.replace(_PARSE_FORMAT, function(m, aidx, flag, width, prec,
                                                size, types) {
    var v, w = _DECODE[types];

    idx = aidx ? parseInt(aidx) : next++;

    !(w & _NOINDEX) && (v = (av[idx] === void 0) ? "undefined" : av[idx]);
    w & _NUMBER  && (v = parseInt(v));
    w & _FLOAT   && (v = parseFloat(v));
    w & _LITERAL && (v = ((types === "s" ? v : types) || "").toString());
    if (w & (_NUMBER | _FLOAT) && isNaN(v)) { return ""; }

    w & _UNSIGNED&& (v = (v >= 0) ? v : v % _UNSIGNED_NUM + _UNSIGNED_NUM);
    w & _OCTET   && (v = v.toString(8));
    w & _HEX     && (v = v.toString(16));
    w & _PREFIX  && (flag === "#") && (v = ((w & _OCTET) ? "0" : "0x") + v);
    w & _PRECIS  && prec &&
                    (v = (w & _FLOAT) ? v.toFixed(prec) : v.substring(0, prec));
    w & _TONUM   && (v = (typeof v !== "number" || v < 0) ? "" : charCode(v));
    w & _TOASCII && (v = (typeof v !== "number" || v < 0) ? "" : (v < 32 || v > 126)
                                                          ? "." : charCode(v));
    w & _IGZERO  && (flag = (flag === "0") ? "" : flag);
    v = w & _TOUPPER ? v.toString().toUpperCase() : v.toString();
    if (w & _NOWIDTH || width === void 0 || v.length >= width) {
      return v;
    }
    // -- pad zero or space ---
    flag = flag || " ";
    size = width - v.length;

    if (flag === "0" && (w & _SIGNED) && v.indexOf("-") !== -1) {
      // "-123" -> "-00123"
      return "-" + Array(size + 1).join("0") + v.substring(1);
    }
    return Array(size + 1).join((flag === "#") ? " " : flag) + v;
  });
}

uuMeta.mix(String.prototype, {
  fmt: StringFormat
}, 0, 0);

})(); // uuMix.string scope

